home *** CD-ROM | disk | FTP | other *** search
- ## -*-Tcl-*-
- # ###################################################################
- # TclAE - AppleEvent extension for Tcl
- #
- # FILE: "aeom.tcl"
- # created: 11/15/2000 {5:54:56 PM}
- # last update: 2/7/2001 {10:03:25 AM}
- # Author: Jonathan Guyer
- # E-mail: jguyer@his.com
- # mail: POMODORO no seisan
- # www: http://www.his.com/jguyer/
- #
- # ========================================================================
- # Copyright © 2000 Jonathan Guyer
- # All rights reserved
- # ========================================================================
- # Permission to use, copy, modify, and distribute this software and its
- # documentation for any purpose and without fee is hereby granted,
- # provided that the above copyright notice appear in all copies and that
- # both that the copyright notice and warranty disclaimer appear in
- # supporting documentation.
- #
- # Jonathan Guyer disclaims all warranties with regard to this software,
- # including all implied warranties of merchantability and fitness. In
- # no event shall Jonathan Guyer be liable for any special, indirect or
- # consequential damages or any damages whatsoever resulting from loss of
- # use, data or profits, whether in an action of contract, negligence or
- # other tortuous action, arising out of or in connection with the use or
- # performance of this software.
- # ========================================================================
- # Description:
- #
- # Implementation of Alpha's AppleEvent Object Model.
- #
- # History
- #
- # modified by rev reason
- # ---------- --- --- -----------
- # 2000-11-15 JEG 1.0 original
- # ###################################################################
- ##
-
- alpha::extension aeom 1.0a2 {
- alpha::package require tclAE
-
- if {[info tclversion] >= 8.0
- && ![catch {namespace eval :: {package require tclAE}}]} {
- aeom::defineAlpha7CommandsForTclAE
- } else {
- aeom::defineTclAECommandsForAlpha7
- }
-
- if {![catch {alpha::package require Alpha 8.0d17}]} {
- aeom::accessor::registerAll
- }
- } maintainer {
- "Jon Guyer" <jguyer@his.com> <http://www.his.com/jguyer/>
- } help {
- Implementation of Alpha's AppleEvent Object Model. This
- package is necessary for Alpha to work properly.
- }
-
-
- namespace eval aeom {}
-
- # ◊◊◊◊ initialization ◊◊◊◊ #
-
- proc aeom::_complainNoAE {} {
- global HOME
- alertnote "There appears to be no mechanism for sending \
- AppleEvents. Alpha will not be able to communicate with \
- other applications unless TclAE.shlb is placed in ${HOME}: or \
- in :Tool Command Language:."
- }
-
- proc aeom::defineAlpha7CommandsForTclAE {} {
-
- if {[llength [info commands "::tclAE::send"]] == 0} {
- aeom::_complainNoAE
- } else {
-
- if {[llength [info commands "::AEBuild"]] == 0} {
- # AEBuild has been removed in Alpha 8, but needs to exist for
- # legacy code. Patch it through to tclAE::send.
-
- ;proc ::AEBuild {args} {
- # AEBuild expects an AEGizmos AEPrint string
- eval tclAE::send -p $args
- }
- }
-
- if {[llength [info commands "::dosc"]] == 0} {
- # dosc has been removed in Alpha 8, but needs to exist for
- # legacy code. Patch it through to tclAE::send.
- ;proc ::dosc {args} {
- set opts(-k) "'misc'"
- set opts(-e) "'dosc'"
-
- set opts(-t) 0
- set opts(-r) 0
- set opts(-q) 0
-
- getOpts {c n k e s f t}
-
- set dosc {tclAE::send}
-
- # set reply form
- if {$opts(-q)} {
- # queue
- set dosc {tclAE::send -q}
- } elseif {!$opts(-r)} {
- # directly (-r is backwards)
- set dosc {tclAE::build::resultData}
-
- if {$opts(-t) > 0} {
- # set timeout
- lappend dosc -t $opts(-t)
- }
- } else {
- set dosc {tclAE::send}
- }
-
- # set target
- if {[info exists opts(-c)]} {
- # by creator
- lappend dosc $opts(-c)
- } elseif {[info exists opts(-n)]} {
- # by name
- lappend dosc $opts(-n)
- } else {
- # prompt user
- set target [tclAE::PPCBrowser]
- }
-
- regexp {^'([^']*)'$} $opts(-k) blah class
- lappend dosc $class
-
- regexp {^'([^']*)'$} $opts(-e) blah event
- lappend dosc $event
-
- if {[info exists opts(-s)]} {
- lappend dosc ---- [tclAE::build::TEXT $opts(-s)]
- } elseif {[info exists opts(-f)]} {
- lappend dosc ---- [tclAE::build::alis $opts(-f)]
- } else {
- error "You must supply either a script or a file path"
- }
-
- set result [eval $dosc]
- }
- }
- }
-
-
- # In Alpha 8, this is an internal command, so it will
- # already exist. Earlier Alphas need to have the proc created here.
- if {[llength [info commands "::tclAE::installEventHandler"]] == 0} {
- aeom::_complainNoAE
- } else {
- if {[llength [info commands "::eventHandler"]] == 0} {
- # eventHandler has been removed in Alpha 8, but needs to exist for
- # legacy code. Patch it through to tclAE::installEventHander.
-
- ;proc ::eventHandler {args} {
- eval tclAE::installEventHandler $args
- }
- }
- }
-
- tclAE::installEventHandler aevt oapp aeom::handleOpenApp
- tclAE::installEventHandler aevt odoc aeom::handleOpen
- tclAE::installEventHandler aevt pdoc aeom::handlePrint
- tclAE::installEventHandler aevt quit aeom::handleQuit
-
- tclAE::installEventHandler misc dosc aeom::handleDoScript
-
- tclAE::aete::register aeom::constructAETE
-
- # resumeHandlingAppleEvents
- }
-
-
- proc aeom::defineTclAECommandsForAlpha7 {} {
- alpha::package require binary
-
- # In Alpha 8, this is an internal command, so it will
- # already exist. Earlier Alpha's need to have the proc created here.
- if {[llength [info commands "tclAE::send"]] == 0} {
- if {[llength [info commands "AEBuild"]] > 0} {
-
- # -r: direct reply requested
- # -Q <proc>: queued reply requested (handler proc specified directly)
- # -q: queued reply requested (register handler with currentReplyHandler)
- # -p: print reply with AEPrint before returning it (if absent, return parsed AEDesc identifier).
- # -t <timeout>: specifies event timeout in ticks
- ;proc tclAE::send {args} {
- global tclAE::directQueueHandlers
-
- set opts(-r) 0
- set opts(-q) 0
- set opts(-p) 0
-
- getOpts {t Q}
-
- set send {AEBuild}
-
- # set reply form
- if {[info exists opts(-Q)]} {
- # queue
- # this isn't quite right.
- # <proc> is expecting TclAE descriptors,
- # but replyHandler will give it AEGizmos.
- currentReplyHandler tclAE::directQueueHandler 1
- lappend tclAE::directQueueHandlers $opts(-Q)
- lappend send -q
- unset opts(-Q)
- }
-
- if {[info exists opts(-t)]} {
- lappend send -t $opts(-t)
- unset opts(-t)
- }
-
- if {$opts(-r)} {
- lappend send -r
- } elseif {$opts(-q)} {
- lappend send -q
- } elseif {$opts(-p)} {
- lappend send -p
- }
-
- set event [eval [concat $send $args]]
-
- if {$opts(-p)} {
- return $event
- } elseif {$opts(-r)} {
- return [tclAE::parse::event $event]
- } else {
- return
- }
- }
-
- proc tclAE::directQueueHandler {queue} {
- global tclAE::directQueueHandlers
-
- # Something's goofy with the
- # form of queue as returned by AEPrint
- regsub -all {\\\{} $queue "{" queue
- regsub -all {\\\}} $queue "}" queue
-
- # parse the event and display any errors
- set queueDesc [tclAE::parse::event $queue]
- set dummyDesc [tclAE::createDesc null]
-
- set handled 0
- foreach handler ${tclAE::directQueueHandlers} {
- if {![catch {$handler $queueDesc $dummyDesc}]} {
- set tclAE::directQueueHandlers \
- [lremove ${tclAE::directQueueHandlers} $handler]
- set handled 1
- }
- }
-
- tclAE::disposeDesc $queueDesc
- tclAE::disposeDesc $dummyDesc
- return $handled
- }
- } else {
- aeom::_complainNoAE
- }
- }
-
-
- # In Alpha 8, this is an internal command, so it will
- # already exist. Earlier Alpha's need to have the proc created here.
- if {[llength [info commands "tclAE::installEventHandler"]] == 0} {
- if {[llength [info commands "eventHandler"]] > 0} {
- ;namespace eval tclAE {}
-
- ;proc tclAE::installEventHandler {args} {
- eval eventHandler $args
- }
- } else {
- aeom::_complainNoAE
- }
- }
-
- # tclAE::target only exists in Alpha 8
- if {[llength [info commands "tclAE::target"]] == 0} {
- if {[cache::exists tclAETargets]} {
- message "Restoring AE Targets…"
-
- cache::read tclAETargets
- foreach targetArray [info locals target*] {
- # Need to write target set commands
-
- set target [set ${targetArray}(hashKey)]
- unset ${targetArray}(hashKey)
- foreach keyword [array names $targetArray] {
- # Copy target information into internal hash table
- tclAE::target set $target $keyword \
- [set ${targetArray}($keyword)]
- }
-
- # We probably don't really need to do this, since it's local
- unset $targetArray
- }
- }
- }
- }
-
- # ◊◊◊◊ Required AppleEvent Handlers ◊◊◊◊ #
-
- proc aeom::handleOpenApp {theAppleEvent theReplyAE} {
-
- }
-
- proc aeom::handleQuit {theAppleEvent theReplyAE} {
- # alertnote [tclAE::print $theReplyAE]
- quit
- }
-
- proc aeom::handlePrint {theAppleEvent theReplyAE} {
-
- set theAESubDesc [tclAE::subdesc::fromDesc $theAppleEvent]
- tclAE::subdesc::getKey $theAESubDesc ---- 1
- set paths [aeom::_extractPaths $theAESubDesc]
- tclAE::subdesc::dispose $theAESubDesc
-
- foreach path $paths {
- set winNum [lsearch -exact [winNames -f] $path]
- if { $winNum < 0 } {
- set winNum [lsearch [winNames -f] "[quote::Glob $path] <*>"]
- }
-
- edit -c $path
- catch {::print}
-
- if {$winNum < 0} {
- # Window was only opened for the print command
- killWindow
- }
- }
- }
-
- proc aeom::handleOpen {theAppleEvent theReplyAE} {
- set theAESubDesc [tclAE::subdesc::fromDesc $theAppleEvent]
- tclAE::subdesc::getKey $theAESubDesc ---- 1
- set paths [aeom::_extractPaths $theAESubDesc]
- tclAE::subdesc::dispose $theAESubDesc
-
- if {[catch {tclAE::getKeyData $theAppleEvent perm} allWritable]} {
- set allWritable "yes "
- }
- if {[catch {tclAE::getKeyData $theAppleEvent Wrap} allWrapped]} {
- set allWrapped "no "
- }
- if {[catch {tclAE::getKeyData $theAppleEvent NewW} allNewWins]} {
- set allNewWins "no "
- }
- # horrible, Horrible, HORRIBLE position specifier
- # designed by THINK, but used by OzTeX (maybe others?)
- if {![catch {tclAE::getKeyData $theAppleEvent kpos ????} THINKPosInfo]} {
- binary scan $THINKPosInfo SSIIII THINKshowMsg THINKline THINKstart THINKend THINKerrmsgH THINKfileModDate
-
- set gotTHINKposition 1
- } else {
- set gotTHINKposition 0
- }
-
- foreach path $paths {
- set parameters {}
-
- set wrapit $allWrapped
- set writable $allWritable
- set newWin $allNewWins
-
- set windows [winNames -fnocount]
- set winNum [lsearch -exact $windows $path]
-
- if {$winNum >= 0
- && $newWin == "ask "} {
- if {[askyesno "Do you want another copy of ‘[file tail $path]’?"] == "yes"} {
- set newWin "yes "
- } else {
- set newWin "no "
- }
- }
-
- if {$winNum >= 0
- && $newWin == "no "} {
- bringToFront [lindex $windows $winNum]
- unset writable
- unset wrapit
- } else {
- openFile $path
-
- getWinInfo flags
-
- if {${flags(hasSpurious)}
- && $writable == "ask "} {
- set lockit [alert -t stop -k "Lock File" -c "Allow Save" -o "" \
- "The file ‘[file tail $path]’ had inconsistent line terminations. \
- They have been converted to Carriage Returns." \
- "Saving this file may damage it if it was binary data."]
-
- if {$lockit == "Lock File"} {
- set writable "no "
- } else {
- set writable "yes "
- }
- }
-
- if {$writable == "no "} {
- setWinInfo read-only 1
- unset wrapit
- } else {
- if {${flags(needsWrap)}
- && $wrapit == "ask "} {
- set doWrap [alert -t caution -k "Wrap It" -c "Leave It Alone" -o "" \
- "Wrap ‘[file tail $path]’?" \
- "This will remove the paragraph formatting from the file."]
-
- if {$doWrap == "Wrap It"} {
- set wrapit "yes "
- } else {
- set wrapit "no "
- }
- }
-
- if {${flags(needsWrap)}
- && $wrapit == "yes "} {
- set savePos [getPos]
- wrapText [minPos] [maxPos]
- goto $savePos
- setWinInfo needsWrap 0
- }
- }
- }
-
- if {$gotTHINKposition} {
- if {$THINKline >= 0} {
- set minRowCol [posToRowCol [minPos]]
- incr THINKline [lindex $minRowCol 0]
- goto [rowColToPos $THINKline 0]
- nextLineSelect
- centerRedraw
-
- if {$THINKshowMsg} {
- alert -t stop -c "" -o "" "@#*!% THINK error" \
- [format "Error message handle address 0x%08X" $THINKerrmsgH]
- }
- } else {
- select $THINKstart $THINKend
- centerRedraw
- }
- }
-
-
- if {[info exists newWin]} {
- lappend parameters NewW $newWin
- }
- if {[info exists writable]} {
- lappend parameters perm $writable
- }
- if {[info exists wrapit]} {
- lappend parameters Wrap $wrapit
- }
-
- lappend sortedPaths(${parameters}) $path
-
- }
-
- # if kAEDirectCall
- # for recording purposes only
- if {[tclAE::getAttributeData $theAppleEvent esrc] == 1} {
- foreach condition [array names sortedPaths] {
- set pathList [tclAE::createList]
- foreach path [set sortedPaths($condition)] {
- tclAE::putDesc $pathList -1 [tclAE::build::alis $path]
- }
- eval tclAE::send -s -dx aevt odoc ---- $pathList $condition
- tclAE::disposeDesc $pathList
- }
- }
-
- return
- }
-
-
- proc aeom::handleAnswer {theAppleEvent theReplyAE} {
- if {![catch {tclAE::getKey $theAppleEvent CERR} errorList]} {
- think::parseCompileErrors $errorList
- } else {
- handleReply [tclAE::print $theAppleEvent]
- }
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "aeom::handleDoScript" --
- #
- # The following routine handles the misc dosc event which your application
- # should support. How you integrate it into your app depends largely on the
- # structure of said app. I have installed it by adding a DoAppleEvent method
- # to my application subclass which checks each AppleEvent to see if it is
- # 'misc' 'dosc'. If so, this routine is called. CUSTOM */
- # -------------------------------------------------------------------------
- ##
- proc aeom::handleDoScript {theAppleEvent theReplyAE} {
- set scriptDesc [tclAE::getKeyDesc $theAppleEvent ----]
- set script [tclAE::getData $scriptDesc TEXT]
- set type [tclAE::getDescType $scriptDesc]
- tclAE::disposeDesc $scriptDesc
-
- switch -- $type {
- "TEXT" {
- eval $script
- }
- "alis" {
- source $script
- }
- default {
- set errn -1770
- set errs "AEDoScriptHandler: invalid script type '${type}', \
- must be 'alis' or 'TEXT'"
- message $errs
-
- tclAE::putKeyData $theReplyAE errs TEXT $errs
- tclAE::putKeyData $theReplyAE errn long $errn
-
- return $errn
- }
- }
- }
-
- proc aeom::constructAETE {} {
- set suites {}
- set events {}
- set parameters {}
- set enumerations {}
- set enumerators {}
-
- lappend enumerators [list "yes" "yes " "take the action"]
- lappend enumerators [list "no" "no " "do not take the action"]
- lappend enumerators [list "ask" "ask " "ask the user whether to take the action"]
-
- lappend enumerations [list savo $enumerators]
-
- lappend parameters [list "new window" NewW savo \
- "whether to open file in a new window. (default: no)" 101]
- lappend parameters [list "protecting bad line endings" perm savo \
- "whether to allow saving a file with inconsistent line endings. \
- (default: yes)" 101]
- lappend parameters [list "wrapping" Wrap savo \
- "whether to hard wrap the file. (default: no)" 101]
-
- lappend events [list "open" "open document" aevt odoc \
- {null "" 000} {alis "the file to open" 0001} $parameters]
-
- lappend suites [list "Standard Suite" "Common terms for most applications" \
- CoRe 1 1 $events {} {} $enumerations]
-
-
- set events {}
- set enumerations {}
- set enumerators {}
-
- lappend enumerators [list "Tcl instructions" TEXT "Tcl script code to execute"]
- lappend enumerators [list "alias" alis "alias of a .tcl script file to source"]
-
- lappend enumerations [list ScAl $enumerators]
-
- lappend events [list "do script" \
- "Execute a Tcl (Tool Command Language) script" misc dosc \
- {null "" 000} {ScAl "the Tcl script to execute" 0011}]
-
- lappend suites [list "Miscellaneous Standards Suite" \
- "Useful events that aren’t in any other suite." \
- misc 1 1 $events {} {} $enumerations]
-
- return [list 1 0 0 0 $suites]
- }
-
- proc aeom::_extractPath {alis} {
- switch [tclAE::subdesc::getType $alis] {
- "obj " {
- set alisDesc [tclAE::subdesc::toDesc $alis alis]
- }
- "alis" {
- set alisDesc [tclAE::subdesc::toDesc $alis]
- }
- }
-
- set path [tclAE::getData $alisDesc TEXT]
-
- tclAE::disposeDesc $alisDesc
-
- return $path
- }
-
- proc aeom::_extractPaths {alises} {
-
- set paths {}
-
- switch -- [tclAE::subdesc::getType $alises] {
- "list" {
- set count [tclAE::countItems $alises]
-
- for {set item 0} {$item < $count} {incr item} {
- set alis [tclAE::subdesc::getNth $alises $item]
-
- lappend paths [aeom::_extractPath $alis]
-
- tclAE::subdesc::dispose $alis
- }
- }
- default {
- lappend paths [aeom::_extractPath $alises]
- }
- }
-
- return $paths
- }
-
- # ◊◊◊◊ Object Accessors ◊◊◊◊ #
-
- namespace eval aeom::accessor {}
-
- proc aeom::accessor::registerAll {} {
- tclAE::installObjectAccessor cwin null aeom::accessor::cwin<null
- tclAE::installObjectAccessor cwor WIND aeom::accessor::cwor<WIND
- tclAE::installObjectAccessor cwor CHAR aeom::accessor::cwor<CHAR
- }
-
- # tclAE::resolve [tclAE::build::indexObject cwor 1 [tclAE::build::winByName aeom.tcl]]
-
- proc aeom::accessor::cwin<null {desiredClass containerToken containerClass keyForm keyData theToken} {
- set wins [winNames]
-
- switch -- $keyForm {
- "name" {
- set winNum [lsearch $wins [tclAE::getData $keyData TEXT]]
- if {$winNum < 0} {
- error::throwOSErr –1728
- }
- }
- "indx" {
- # absolute positions are 1-based
- set winNum [expr {[tclAE::getData $keyData long] - 1}]
-
- if {($winNum >= [llength $wins]) || ($winNum < 0)} {
- error::throwOSErr –1728
- }
- }
- default {
- error::throwOSErr –1708
- }
- }
- tclAE::replaceDescData $theToken WIND [lindex $wins $winNum]
- }
-
- proc aeom::accessor::_cwor {win start stop keyForm keyData theToken} {
- set mode [win::FindMode $win]
- set wordBreak [mode::getVar wordBreak $mode]
- set wordBreakPreface [mode::getVar wordBreakPreface $mode]
-
- switch -- $keyForm {
- "indx" {
- set index [tclAE::getData $keyData long]
- if {$index > 0} {
- # forward search from start of range
- for {} {$index > 0} {incr index -1} {
- if {[catch {search -w $win -f 1 -r 1 -l $stop -- "$wordBreak" $start} word]} {
- # errAENoSuchObject
- error::throwOSErr -1728
- }
- set start [lindex $word 1]
- }
- set start [lindex $word 0]
- set stop [lindex $word 1]
- } else {
- # backward search from end of range
- for {} {$index < 0} {incr index} {
- if {[catch {search -w $win -f 0 -r 1 -l $start -- "$wordBreakPreface$wordBreak" [pos::math $stop - 1]} word]} {
- # errAENoSuchObject
- error::throwOSErr -1728
- }
- set stop [lindex $word 0]
- }
- set start [pos::math [lindex $word 0] + 1]
- set stop [lindex $word 1]
- }
- }
- "rang" {
- set boundaries [aeom::accessor::_getBoundaries $keyData]
- set startItem [lindex $boundaries 0]
- set stopItem [lindex $boundaries 1]
-
- set startData [tclAE::getData $startItem TEXT]
- if {[tclAE::getDescType $startItem] == "CHAR"} {
- set start [lindex $startData 1]
- } else {
- set start [lindex $startData 2]
- }
- tclAE::disposeDesc $startItem
-
- set stopData [tclAE::getData $stopItem TEXT]
- if {[tclAE::getDescType $stopItem] == "CHAR"} {
- set stop [lindex $stopData 2]
- } else {
- set stop [lindex $stopData 1]
- }
- tclAE::disposeDesc $stopItem
- }
- "rele" {
-
- }
- default {
- error::throwOSErr –1708
- }
- }
-
- tclAE::replaceDescData $theToken CHAR [list $win $start $stop]
- }
-
- proc aeom::accessor::_getBoundaries {rangeDesc} {
- # is it really necessary to coerce this? gross
- set rangeRecord [tclAE::coerceDesc $rangeDesc reco]
-
- set startDesc [tclAE::getKeyDesc $rangeRecord star]
- set startItem [tclAE::resolve $startDesc]
- tclAE::disposeDesc $startDesc
-
- set stopDesc [tclAE::getKeyDesc $rangeRecord stop]
- set stopItem [tclAE::resolve $stopDesc]
- tclAE::disposeDesc $stopDesc
-
- tclAE::disposeDesc $rangeRecord
-
- return [list $startItem $stopItem]
- }
-
- proc aeom::accessor::cwor<WIND {desiredClass containerToken containerClass keyForm keyData theToken} {
- set win [tclAE::getData $containerToken TEXT]
- set start [minPos]
- set stop [maxPos -w $win]
-
- aeom::accessor::_cwor $win $start $stop $keyForm $keyData $theToken
- }
-
- proc aeom::accessor::cwor<CHAR {desiredClass containerToken containerClass keyForm keyData theToken} {
- set charData [tclAE::getData $containerToken TEXT]
- set win [lindex $charData 0]
- set start [lindex $charData 1]
- set stop [lindex $charData 2]
-
- aeom::accessor::_cwor $win $start $stop $keyForm $keyData $theToken
- }
-
-
-